package genotype;

import java.util.Arrays;

import evolutionaryLoop.EvolutionaryParameters;
import evolutionaryProblems.Phenotype;
import flatlandAgent.FlatlandPhenotype;

public class FlatlandGenotype extends ArrayGenotype1D<Double> {

	private int[] neuronsPerLayer;
	private final int MAX_WEIGHT = 1;
	private final int MIN_WEIGHT = -1;
	
//	public static void main(String[] args) {
//		int[] neuronsPerLayer = {3, 5, 3};
//		EvolutionaryParameters parameters = new EvolutionaryParameters();
//		FlatlandGenotype genotype = new FlatlandGenotype(neuronsPerLayer, parameters);
//		System.out.println(genotype.toString());
//	}

	public FlatlandGenotype(int[] neuronsPerLayer, EvolutionaryParameters parameters) {
		super(Double.class, getNofweights(neuronsPerLayer), parameters);
		this.neuronsPerLayer = neuronsPerLayer;
		initialiseGene(neuronsPerLayer);
	}

	private void initialiseGene(int[] nodesPerLayer) {
		int nofNodesNextLayer;
		int nofNodesThisLayer;
		int currentWeight = 0;
		double[] weights;

		for (int i = 0; i < nodesPerLayer.length; i++) {
			if (i == nodesPerLayer.length-1) nofNodesNextLayer = 0;
			else nofNodesNextLayer = nodesPerLayer[i+1];
			nofNodesThisLayer = nodesPerLayer[i];
			
			for (int j = 0; j < nofNodesThisLayer; j++) {
				weights = createWeights(nofNodesNextLayer);
				
				for (int k = 0; k < weights.length; k++) {
					set(weights[k], currentWeight);
					currentWeight++;
				}
			}
		}
	}

	private double[] createWeights(int nofNodesNextLayer) {
		double[] weights = new double[nofNodesNextLayer+1];
		for (int i = 0; i < weights.length-1; i++) {
			weights[i] = MIN_WEIGHT + (Math.random() * ((MAX_WEIGHT - MIN_WEIGHT) + 1));
		}
		weights[weights.length-1] = MIN_WEIGHT + (Math.random() * ((MAX_WEIGHT - MIN_WEIGHT) + 1)); // firing threshold
		return weights;
	}
	
	private static int getNofweights(int[] neuronsPerLayer) {
		int weights = 0;
		int nofNodesNextLayer;
		int nofNodesThisLayer;
		
		for (int i = 0; i < neuronsPerLayer.length; i++) {
			nofNodesThisLayer = neuronsPerLayer[i];
			if (i == neuronsPerLayer.length-1) nofNodesNextLayer = 0;
			else nofNodesNextLayer = neuronsPerLayer[i+1];
			
			weights += nofNodesNextLayer*nofNodesThisLayer;
		}
		
		return weights + getNofNodes(neuronsPerLayer);
	}

	private static int getNofNodes(int[] neuronsPerLayer) {
		int nofNodes = 0;
		for (int i = 0; i < neuronsPerLayer.length; i++) {
			nofNodes += neuronsPerLayer[i];
		}
		return nofNodes;
	}

	@Override
	public Phenotype evolveGenotype() {
		return new FlatlandPhenotype(this);
	}

	@Override
	public Double mutateValue(Double value) {
		return MIN_WEIGHT + (Math.random() * ((MAX_WEIGHT - MIN_WEIGHT) + 1));
	}

	@Override
	public ArrayGenotype1D<Double> clone() {
		FlatlandGenotype clone = new FlatlandGenotype(neuronsPerLayer, parameters); 
		for (int i = 0; i < length(); i++) {
			clone.set(get(i), i); 
		}
		return clone;
	}

	@Override
	public ArrayGenotype1D<Double> createNewInstance(int length) {
		return new FlatlandGenotype(neuronsPerLayer, parameters);
	}
	
	public int[] getNeuronsPerLayer(){
		return neuronsPerLayer; 
	}
	
	public String toString() {
		return Arrays.toString(getArrayGenotype());
	}

}
